package top.zhan.singleton;

/**
 * 单例设计模式 - 懒汉实现方式
 * @author Zhan
 * @date 2022/9/26 13:07
 */
public class SingletonLazy {

//    private static SingletonLazy instance;

    /**
     * 构造函数私有化
     */
    private SingletonLazy(){}

    /**
     * 单例对象的方法
     */
    public void process(){
        System.out.println("方法调用成功");
    }

    /**
     * 第一种方式
     * 对外暴露一个方法获取类的对象
     * 线程不安全，多线程下面存在安全问题
     * @return SingletonLazy
     */
//    public static SingletonLazy getInstance(){
//        if(instance == null){
//            instance = new SingletonLazy();
//        }
//        return instance;
//    }

    /**
     * 第二种方式：
     * 通过加锁 synchronized 保证单例
     *
     * 采用synchronized对方法加锁有很大的性能开销
     *
     * 解决办法：锁粒度放小
     * @return SingletonLazy
     */
//    public static synchronized SingletonLazy getInstance(){
//        if(instance == null){
//            instance = new SingletonLazy();
//        }
//        return instance;
//    }

    /**
     * 第三种方式：
     * DCL = 双重检查锁定 （Double-Checked-Locking）
     * 在多线程情况下保持多性能
     *
     * instance = new SingletonLazy();并不是原子性操作
     * 1、分配空间给对象
     * 2、在空间内创建对象
     * 3、将对象的引用赋值给对象
     *
     * 指令重排：
     * 假如线程 1-3-2顺序，会把值写回主内存，其他线程就会读取到instance最新的值，但是这个是不完全的对象
     *
     * @return SingletonLazy
     */
//    public static SingletonLazy getInstance(){
//        if(instance == null){
//            synchronized (SingletonLazy.class){
//                if(instance == null){
//                    instance = new SingletonLazy();
//                }
//            }
//        }
//        return instance;
//    }

    /**
     * volatile 是Java提供的关键词，可以禁止指令重排
     */
    private static volatile SingletonLazy instance;

    public static SingletonLazy getInstance(){
        // 第一重检查
        if(instance == null){
            synchronized (SingletonLazy.class){
                // 第二重检查
                if(instance == null){
                    // volatile 禁止指令重排，保证多线程环境下的线程安全
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
}
